﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Irony.Parsing;

namespace MudEngine.Parser
{
	public class CommandStringGrammar : Grammar
	{
		private NonTerminal Obj;

		public CommandStringGrammar()
			: base(false)
		{
			KeyTerm COMMA = ToTerm(",", ",");
			KeyTerm SPACE = ToTerm(" ", "<SPACE>");

			KeyTerm THE = ToTerm("the", "'the'");
			KeyTerm A = ToTerm("a", "'a'");
			KeyTerm AN = ToTerm("an", "'an'");
			KeyTerm ANY = ToTerm("any", "'any'");
			KeyTerm MY = ToTerm("my", "'my'");
			KeyTerm IN = ToTerm("in", "'in'");
			KeyTerm AT = ToTerm("at", "'at'");
			KeyTerm ON = ToTerm("on", "'on'");
			KeyTerm FROM = ToTerm("from", "'from'");

			var Number = new NumberLiteral("Number", NumberOptions.AllowLetterAfter);
			var Adjective = new CustomTerminal("Adjective", TryMatchAdjective, " ");

			var Obj_Adjective_or_Noun = new IdentifierTerminal("OBJ");

			var Proposition = new NonTerminal("Proposition", "Propositions");
			var Command = new NonTerminal("Command", "Command unit");
			var Qualifier = new NonTerminal("Qualifier");
			Obj = new NonTerminal("OBJ_NonTerminal");

			Obj_Adjective_or_Noun.Priority = 10;
			var Obj_Adjective_or_Noun_list = new NonTerminal("Obj_Adjective_or_Noun_list");
			Obj_Adjective_or_Noun_list.Rule = MakeStarRule(Obj_Adjective_or_Noun_list, Obj_Adjective_or_Noun);

			Obj.Rule = Proposition.Q() + Qualifier.Q() + Obj_Adjective_or_Noun_list;

			this.Root = Command;
			this.LanguageFlags = LanguageFlags.CreateAst;

			Proposition.Rule = THE | A | AN | ANY | MY | IN | AT | ON | FROM;

			Qualifier.Rule = Adjective | ToTerm("all", "'all'");
			this.NonGrammarTerminals.Add(THE);
			this.MarkPunctuation(THE, A, AN, ANY, MY, IN, AT, ON, FROM);
			// new command rule for each verb
			//Command.Rule = "look" | "look" + Obj | "look at" + Obj | "look in" + Obj;
		}
        /// <summary>
        /// 
        /// </summary>
        /// <param name="verb"></param>
        /// <param name="rule"></param>
        public void AddRule(string verb, string rule)
        {
            string[] words = rule.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            BnfExpression bnfRule = new BnfExpression(ToTerm(verb));

            for (int w = 0; w < words.Length; w++)
            {
                switch (words[w])
                {
                    case "{OBJ}": // object in direct environment
                        bnfRule += Obj;
                        break;
                    default:
                        bnfRule += ToTerm(words[w]);
                        break;
                }
            }
            if (this.Root.Rule == null)
                this.Root.Rule = bnfRule;
            else
                this.Root.Rule |= bnfRule;

            //Log.Debug(this.GetType(), string.Format("New grammar: {0}", rule.ToString()));
        }
		/// <summary>
		/// 
		/// </summary>
		/// <param name="terminal"></param>
		/// <param name="context"></param>
		/// <param name="source"></param>
		/// <returns></returns>
		public Token TryMatchAdjective(Terminal terminal, ParsingContext context, ISourceStream source)
		{
			if (char.IsDigit(source.PreviewChar))
			{
				string nr = source.PreviewChar.ToString();
				source.PreviewPosition++;
				while (char.IsDigit(source.PreviewChar))
				{
					nr += source.PreviewChar;
					source.PreviewPosition++;
				}
				int number = int.Parse(nr);
				if ((number == 1 && source.PreviewChar == 's' && source.NextPreviewChar == 't') ||
					 (number == 2 && source.PreviewChar == 'n' && source.NextPreviewChar == 'd') ||
					 (number == 3 && source.PreviewChar == 'r' && source.NextPreviewChar == 'd') ||
					 (number > 3 && source.PreviewChar == 't' && source.NextPreviewChar == 'h'))
				{
					nr += source.PreviewChar;
					nr += source.NextPreviewChar;
					return new Token(terminal, source.Location, nr, number);
				}
			}
			return null;
		}
	}
}
